﻿using TTGame;
using TTGameEngine;
using System.Collections.Generic;
using UnityEngine;

public enum TargetStrategy {
    Default, //不做处理，物理引擎默认的顺序。
    HeroSeatPrior_MonsterNearest, //目标是英雄则是按出战优先级，如果是怪物，则按距离最近优先。
    Nearest, //优先选最近的
    HPPercentLeast, //优先HP百分比最少的
    HPPercentMost,  //优先HP百分比最多的
    SeatPrior,  //英雄出战位优先级（英雄属性，怪物没有就放最后）
}
/// <summary>
///
/// 每个DRCollider只会碰撞同一个对象一次。
///
/// 设置了DRCollider的ScriptExecOrder在SimpleBullet等子弹脚本执行顺序之前。
/// 具体查看Update注释。
/// </summary>
public abstract class DamageReceiverCollider : MonoBehaviour {
    #region Fields
    public static bool IsDrawColliderWire = false;
    public LayerMask layersToHit;

    public DamageReceiverHit onColliderEnter;
    public DamageReceiverHit onColliderExit;

    public TargetStrategy m_targetStrategy = TargetStrategy.Default;
    //tag 位置 方向
    public System.Action<Puppet, string, Vector3, Vector3> OnHitObject;

    protected Dictionary<Collider, bool> colliderDic = new Dictionary<Collider, bool>();
    #endregion Fields

    #region Delegates

    //return:true 碰撞有效。
    public delegate bool DamageReceiverHit(Puppet d, DamageReceiverCollider collider);

    #endregion Delegates

    protected void init() {
        OnHitObject = null;
        onColliderEnter = null;
        onColliderExit = null;
        colliderDic.Clear();
    }

    public int m_colliderCheckCount = 0;
    public int ColliderCheckCount {
        get { return m_colliderCheckCount; }
    }

    public virtual float ColliderRage {
        get {
            Debug.LogErrorFormat("[DamgeReceiverCollider] ColliderRage没有继承");
            return 0;
        }
    }

    List<Puppet> t_colliderObjList = new List<Puppet>();
    protected void AddColliers(Collider[] _colliders) {
        if(_colliders == null || _colliders.Length <= 0) {
            return;
        }
        //mark:youhua TargetStrategy.Default + 碰撞个数，可以优化减少GetPuppetByCollider调用次数。
        //如果碰撞个数和子弹伤害个数一致，那就不需要排序了
        t_colliderObjList.Clear();
        for(int i = 0; i < _colliders.Length; i++) {
            Collider hit = _colliders[i];
            if(hit != null && !colliderDic.ContainsKey(hit)) {
                Puppet hurtObj = ObjectManager.Share().GetPuppetByCollider(hit);
                if(hurtObj != null && hurtObj.IsActived) {
                    colliderDic.Add(hit, true);
                    t_colliderObjList.Add(hurtObj);
                }
            }
        }
        if(t_colliderObjList.Count <= 0) {
            return;
        }

        TargetStrategy tStrategy = m_targetStrategy;
        if(tStrategy == TargetStrategy.HeroSeatPrior_MonsterNearest) {
            tStrategy = t_colliderObjList[0].IsMonster() ? TargetStrategy.Nearest : TargetStrategy.SeatPrior;
        }
        //根据目标策略排序
        switch(tStrategy) {
            case TargetStrategy.Default:
                break;
            case TargetStrategy.Nearest:
                t_colliderObjList.objNearSortAscending(this.transform.position);
                break;
            case TargetStrategy.HPPercentLeast:
                t_colliderObjList.objHpSortAscending();
                break;
            case TargetStrategy.HPPercentMost:
                t_colliderObjList.objHpSortDescending();
                break;
            case TargetStrategy.SeatPrior:
                t_colliderObjList.objSeatPriorSort();
                break;
        }

        for(int i = 0; i < t_colliderObjList.Count; i++) {
            CheckRayCastHit(t_colliderObjList[i]);
        }
    }
    protected void AddCollier(Collider _collider) {
        if(_collider == null) {
            return;
        }

        if(!colliderDic.ContainsKey(_collider)) {
            Puppet hurtObj = ObjectManager.Share().GetPuppetByCollider(_collider);
            if(hurtObj != null) {
                colliderDic.Add(_collider, true);
                CheckRayCastHit(hurtObj);
            }
        }
    }

    #region Methods
    //旧代码，没用上。
    public abstract bool PointInsideCollider(Vector3 aPoint);

    public abstract void CheckInsideColliderRay();

    //子弹刚产生时处于敌人碰撞框内部，castRay在处于敌人内部时不会产生碰撞。以及子弹刚产生的时候可能不会立刻检测碰撞，而下一帧会飞很远。
    //解决方案：子弹一发射就立马进行一次额外的检测。距离是从人物中心到子弹出生点（枪口），视情况可能还需要加一帧飞行距离。
    public virtual void BeginningColliderRay(Vector3 extraCheckPos) {
        RaycastHit hitInfo;
        if(Physics.Linecast(extraCheckPos, transform.position, out hitInfo, layersToHit.value)) {
            AddCollier(hitInfo.collider);
        }
    }

    protected virtual void OnEnable() {
        m_colliderCheckCount = 0;
        colliderDic.Clear();
    }

    protected void CheckRayCastHit(Puppet hurtObj) {
        bool isColliderValid = false;
        if(onColliderEnter != null) {
            isColliderValid = onColliderEnter(hurtObj, this);
        }

        if(isColliderValid) {
            if(OnHitObject != null) {
                Vector3 impactPosition = hurtObj.objTF.position + new Vector3(0, hurtObj.HitPosHeight, 0);
                impactPosition += (transform.position - impactPosition).normalized * hurtObj.HitPosFactor;
                OnHitObject(hurtObj, hurtObj.tag, impactPosition, (transform.position - impactPosition).normalized);
            }
        }
    }

/// <summary>
/// 为了DRLineCollider的准确性，需在子弹移动前（即子弹的Update前）检测，以保证检测距离和即将移动的距离相同。
/// 因此需要设置DRCollider脚本的【ScriptExecOrder】在SimpleBullet等子弹脚本执行顺序之前。
/// </summary>
    protected virtual void Update() {
        //events
        CheckInsideColliderRay();
        m_colliderCheckCount++;

        //DebugUtil.LogFormat("Collider FixedUpdate. Time.delta[{0}] FixedDelta[{1}] Fixed[{2}]", Time.deltaTime, Time.fixedDeltaTime, Time.fixedTime);
    }

    #endregion Methods
}